<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="chinese">
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">



  
  
    
      
    
    
      
    
  <script src="//cdn.jsdelivr.net/npm/pace-js@1/pace.min.js"></script>
  <link href="//cdn.jsdelivr.net/npm/pace-js@1/themes/blue/pace-theme-minimal.min.css" rel="stylesheet">







<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT">










<meta name="description" content="第二十一章 并发目前为止，你学到的都是顺序编程知识，即程序重的所有事物在任意时刻都只能执行一个步骤。 21.1 并发的多面性用并发解决问题大体上分为：“速度”和“设计可管理性”两种。 21.1.1 更快的执行多处理器可以将大量用户的需求分不到多个CPU中，但是并发通常是提高运行在单处理器上的程序。在单处理器上运行的并发程序开销确实应该比大部分顺序执行开销大。因为增加了上下文切换的代价。如果没有阻塞">
<meta name="keywords" content="Java Php hexo Thinking in Java">
<meta property="og:type" content="article">
<meta property="og:title" content="Thinking in Java 第二十一章">
<meta property="og:url" content="http://yoursite.com/2020/03/03/Thinking-in-Java-第二十一章/index.html">
<meta property="og:site_name" content="坚持就是胜利">
<meta property="og:description" content="第二十一章 并发目前为止，你学到的都是顺序编程知识，即程序重的所有事物在任意时刻都只能执行一个步骤。 21.1 并发的多面性用并发解决问题大体上分为：“速度”和“设计可管理性”两种。 21.1.1 更快的执行多处理器可以将大量用户的需求分不到多个CPU中，但是并发通常是提高运行在单处理器上的程序。在单处理器上运行的并发程序开销确实应该比大部分顺序执行开销大。因为增加了上下文切换的代价。如果没有阻塞">
<meta property="og:locale" content="chinese">
<meta property="og:updated_time" content="2020-03-12T04:23:37.245Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Thinking in Java 第二十一章">
<meta name="twitter:description" content="第二十一章 并发目前为止，你学到的都是顺序编程知识，即程序重的所有事物在任意时刻都只能执行一个步骤。 21.1 并发的多面性用并发解决问题大体上分为：“速度”和“设计可管理性”两种。 21.1.1 更快的执行多处理器可以将大量用户的需求分不到多个CPU中，但是并发通常是提高运行在单处理器上的程序。在单处理器上运行的并发程序开销确实应该比大部分顺序执行开销大。因为增加了上下文切换的代价。如果没有阻塞">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":14,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '190coder'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://yoursite.com/2020/03/03/Thinking-in-Java-第二十一章/">





  <title>Thinking in Java 第二十一章 | 坚持就是胜利</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="chinese">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">坚持就是胜利</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            Home
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            Categories
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            Archives
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2020/03/03/Thinking-in-Java-第二十一章/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="190coder">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="坚持就是胜利">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">Thinking in Java 第二十一章</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-03-03T10:24:28+08:00">
                2020-03-03
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Java/" itemprop="url" rel="index">
                    <span itemprop="name">Java</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h1 id="第二十一章-并发"><a href="#第二十一章-并发" class="headerlink" title="第二十一章 并发"></a>第二十一章 并发</h1><p>目前为止，你学到的都是顺序编程知识，即程序重的所有事物在任意时刻都只能执行一个步骤。</p>
<h2 id="21-1-并发的多面性"><a href="#21-1-并发的多面性" class="headerlink" title="21.1 并发的多面性"></a>21.1 并发的多面性</h2><p>用并发解决问题大体上分为：“速度”和“设计可管理性”两种。</p>
<h4 id="21-1-1-更快的执行"><a href="#21-1-1-更快的执行" class="headerlink" title="21.1.1 更快的执行"></a>21.1.1 更快的执行</h4><p>多处理器可以将大量用户的需求分不到多个CPU中，但是并发通常是提高运行在单处理器上的程序。在<strong>单处理器上运行的并发程序开销确实应该比大部分顺序执行开销大</strong>。因为增加了上下文切换的代价。<strong>如果没有阻塞，那么在单处理机器上使用并发就没有任何意义。</strong><br>在单处理系统中的性能提高的常见示例是事件驱动编程。通过创建单独的执行线程来响应用户的输入，即使这个线程在大多数时间里都是阻塞的，但是程序可以保证具有一定程度的可响应性。<br>java 不支持多任务，Java 添加多线程机制。</p>
<h4 id="21-1-2-改进代码设计"><a href="#21-1-2-改进代码设计" class="headerlink" title="21.1.2 改进代码设计"></a>21.1.2 改进代码设计</h4><ul>
<li>在Java中，通常要假定你不会获得足够的线程，从而使得可以为大型仿真中的每个元素都提供一个线程。</li>
<li>解决这个问题的典型方式是使用协作多线程。Java的线程机制是抢占式的，这表示调度机制会周期性地中断线程，将上下文切换到另一个线程，从而为每个线程都提供时间片，使得每个线程都会分配到数量合理的时间去驱动它的任务。</li>
<li>协作式系统的优势：上下文切换的开销通常比抢占式系统要低廉许多，并且对可以同时执行的线程数量在理论上没有任何限制。</li>
</ul>
<h2 id="21-2-基本的线程机制"><a href="#21-2-基本的线程机制" class="headerlink" title="21.2 基本的线程机制"></a>21.2 基本的线程机制</h2><p>使用线程机制是一种建立透明的、可扩展的程序方法，如果程序运行太慢，为机器添加cpu。多任务和多线程往往是多处理器系统的最合理方式。</p>
<h4 id="21-2-1-定义任务"><a href="#21-2-1-定义任务" class="headerlink" title="21.2.1 定义任务"></a>21.2.1 定义任务</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">package Thread;</span><br><span class="line"></span><br><span class="line">public class LiftOff implements Runnable &#123;</span><br><span class="line"></span><br><span class="line">    protected int CountDown = 10;</span><br><span class="line">    private static int taskCount = 0;</span><br><span class="line">    private final int id = taskCount++;</span><br><span class="line"></span><br><span class="line">    public LiftOff()&#123;&#125;</span><br><span class="line">    public LiftOff(int countDown) &#123;</span><br><span class="line">        CountDown = countDown;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public String status()&#123;</span><br><span class="line">        return &quot;#&quot; + id + &quot;(&quot; + (CountDown &gt; 0 ? CountDown : &quot;off&quot;) + &quot;), &quot;;</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    public void run() &#123;</span><br><span class="line">        while (CountDown-- &gt;0)&#123;</span><br><span class="line"></span><br><span class="line">            System.out.println(status());</span><br><span class="line">            Thread.yield();// 线程调度器，一个线程转移到另一个线程</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">package Thread;</span><br><span class="line"></span><br><span class="line">public class MainThread &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        LiftOff liftOff = new LiftOff();</span><br><span class="line">        liftOff.run();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="21-2-2-Thread-类"><a href="#21-2-2-Thread-类" class="headerlink" title="21.2.2 Thread 类"></a>21.2.2 Thread 类</h4><p>Thread构造器只需要一个Runnable对象。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Thread thread = new Thread(new LiftOff());</span><br><span class="line">thread.start();</span><br></pre></td></tr></table></figure>

<p><strong>main()方法也是一个线程，所以main()和LiftOff()是同时执行的。</strong><br>线程调度机制是非确定性的，不可以依赖线程的代码，尽可能的保守使用线程。<br>线程虽然没有捕获对象的引用，但是注册了自己，所以start()之后仍然不会被垃圾回收。<br><strong>thread的run方法还在执行时候不管这个thread对象处在什么状态下，thread对象都不会被回收，因为Thread的run()方法的局部变量this保持了对线程对象Thread的引用.</strong><br>所以start()仅仅是线程的开始。</p>
<h4 id="21-2-3-使用Executor"><a href="#21-2-3-使用Executor" class="headerlink" title="21.2.3 使用Executor"></a>21.2.3 使用Executor</h4><p>Java SE5的java.util.concurrent中的执行器（Executor）将为你管理Thread对象，从而简化了并发编程。 （线程池）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ExecutorService exec = Executors.newFixedThreadPool(10);</span><br><span class="line">for (int i = 0; i &lt; 5; i++) &#123;</span><br><span class="line">    exec.execute(new LiftOff());</span><br><span class="line">&#125;</span><br><span class="line">exec.shutdown();</span><br></pre></td></tr></table></figure>

<ul>
<li>FixedThreadPool使用了固定的线程集来执行所提交的任务。</li>
<li>CachedThreadPool在程序执行过程中通常会创建与所需数量相同的线程，然后在它回收旧线程时停止创建新线程，因此它是合理的Executor的首选。</li>
<li>SingleThreadExecutor 顺序执行 只会创建一个线程执行 单例线程。</li>
</ul>
<h4 id="21-2-4-从任务中产生返回值"><a href="#21-2-4-从任务中产生返回值" class="headerlink" title="21.2.4 从任务中产生返回值"></a>21.2.4 从任务中产生返回值</h4><p>实现Callable接口 可从任务中产生返回值，submit()会产生Future对象，用Future 对象接收返回参数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">package Thread;</span><br><span class="line"></span><br><span class="line">import java.util.concurrent.Callable;</span><br><span class="line"></span><br><span class="line">public class TaskWithResult implements Callable &#123;</span><br><span class="line">    private int id;</span><br><span class="line"></span><br><span class="line">    public TaskWithResult(int id) &#123;</span><br><span class="line">        this.id = id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public Object call() throws Exception &#123;</span><br><span class="line">        System.out.println(&quot;asd&quot;);</span><br><span class="line">        return &quot;result of TaskWithResult&quot; + id;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">package Thread;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.concurrent.ExecutionException;</span><br><span class="line">import java.util.concurrent.ExecutorService;</span><br><span class="line">import java.util.concurrent.Executors;</span><br><span class="line">import java.util.concurrent.Future;</span><br><span class="line"></span><br><span class="line">public class CallableDemo &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        ExecutorService executorService = Executors.newCachedThreadPool();</span><br><span class="line"></span><br><span class="line">        ArrayList&lt;Future&lt;String&gt;&gt; futures = new ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        Future s = executorService.submit(new TaskWithResult(2));</span><br><span class="line"></span><br><span class="line">        try &#123;</span><br><span class="line">            System.out.println(s.get());</span><br><span class="line">        &#125; catch (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; catch (ExecutionException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        s.isCancelled();</span><br><span class="line">//        for (int i = 0; i &lt; 10; i++) &#123;</span><br><span class="line">//            futures.add(executorService.submit(new TaskWithResult(i)));</span><br><span class="line">//        &#125;</span><br><span class="line">//</span><br><span class="line">//        for (Future&lt;String&gt; fs: futures)&#123;</span><br><span class="line">//            try &#123;</span><br><span class="line">//                System.out.println(fs.get());</span><br><span class="line">//            &#125; catch (InterruptedException e)&#123;</span><br><span class="line">//                System.out.println(e);</span><br><span class="line">//            &#125; catch (ExecutionException e) &#123;</span><br><span class="line">//                e.printStackTrace();</span><br><span class="line">//            &#125; finally &#123;</span><br><span class="line">//                executorService.shutdown();</span><br><span class="line">//            &#125;</span><br><span class="line">//        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>execute提交的方式只能提交一个Runnable的对象，且该方法的返回值是void，也即是提交后如果线程运行后，和主线程就脱离了关系了。 submit 接收 Callable 接口的实现类，可用get()接收返回结果</strong></p>
<h4 id="21-2-5-休眠"><a href="#21-2-5-休眠" class="headerlink" title="21.2.5 休眠"></a>21.2.5 休眠</h4><p>Thread.sleep(1000);</p>
<h4 id="21-2-6-优先级"><a href="#21-2-6-优先级" class="headerlink" title="21.2.6 优先级"></a>21.2.6 优先级</h4><p>绝大时间里，都应该以默认优先级运行。试图操作通常是一种错误。<br>Thread.currentThread().setPriority(Thread.MAX_PRIORITY);<br><strong>优先级在run()开头设置，不要在构造器里设定，因为Executor 还没有开始执行任务</strong><br>只有执行了100000次开销相当大的浮点运算，包括double类型的加法与除法。没有这些运算，看不到优先级的效果。</p>
<h4 id="21-2-7-让步"><a href="#21-2-7-让步" class="headerlink" title="21.2.7 让步"></a>21.2.7 让步</h4><p>如果知道已完成了在run()方法所需的工作，就可以给线程调度一个暗示，告诉它做的差不多了，可以让别的线程使用cpu，这个暗示用yield()方法，不过不能保证他会被采纳。当调用yield时，你也是在建议有相同优先级的其他线程可运行。<br>（yield并不意味着退出和暂停，只是，告诉线程调度如果有人需要，可以先拿去，我过会再执行，没人需要，我继续执行）调用yield的时候锁并没有被释放。对于任何重要的控制或在调整应用时，都不恩那个依赖于yield。实际上，yield经常被误用。</p>
<ul>
<li><strong>解读：不要用yield控制程序，它只是调度线程让步。</strong></li>
</ul>
<h4 id="21-2-8-后台程序"><a href="#21-2-8-后台程序" class="headerlink" title="21.2.8 后台程序"></a>21.2.8 后台程序</h4><p>必须在线程启动之前start()前调用setDaemon()方法，才能把它设置为后台线程。<br>当最后一个非后台线程终止时，后台线程会“突然”停止。<br>isDaemon()来确定线程是否是一个后台线程。如果是 ，它创建的任何线程就是一个后台线程。</p>
<ul>
<li><strong>守护线程不能单独存活在JVM，即当程序中用户线程全部结束后JVM会立即杀死所有守护线程，守护线程中存在finally代码块，那么当所有的非守护线程中止时，守护线程被kill掉，其finally代码块是不会执行的。(除非main（）还在)，所以一般不要使用后台线程，因为你不能优雅的关闭它，非后台的Executor 通常是一种更好的方式。</strong></li>
</ul>
<h4 id="21-2-9-编码的变体"><a href="#21-2-9-编码的变体" class="headerlink" title="21.2.9 编码的变体"></a>21.2.9 编码的变体</h4><p>可直接继承Thread类，但是没办法继承其他类，所以最灵活的还是实现Runnable。</p>
<h4 id="21-2-10-术语"><a href="#21-2-10-术语" class="headerlink" title="21.2.10 术语"></a>21.2.10 术语</h4><p>描述要执行的工作时使用术语 “任务”，只有引用到驱动任务的具体机制时候，才使用“线程”。<br>实现Runnable只是定义一个“任务”，继承Thread 也是 继承出一个任务。在Java中，Thread类自身不执行任何操作，他只是驱动赋予他的任务。<strong>Thread类并没有任何控制权，并且在隔离高的执行器（在执行器里创建任和管理）更加明显。</strong></p>
<h4 id="21-2-11-加入一个线程"><a href="#21-2-11-加入一个线程" class="headerlink" title="21.2.11 加入一个线程"></a>21.2.11 加入一个线程</h4><ul>
<li>join() 定义在Thread.java中。</li>
<li>join() 的作用：让“主线程”等待“子线程”结束之后才能继续运行。</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">// 主线程</span><br><span class="line">public class Father extends Thread &#123;</span><br><span class="line">    public void run() &#123;</span><br><span class="line">        Son s = new Son();</span><br><span class="line">        s.start();</span><br><span class="line">        s.join();</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">// 子线程</span><br><span class="line">public class Son extends Thread &#123;</span><br><span class="line">    public void run() &#123;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在调用s.join()之后，Father主线程会一直等待，直到“子线程s”运行完毕；在“子线程s”运行完毕之后，Father主线程才能接着运行。 这也就是我们所说的“join()的作用，是让主线程会等待子线程结束之后才能继续运行”！</p>
<p><strong>wait()的作用是让“当前线程”等待</strong>，而这里的“当前线程”是指当前在CPU上运行的线程。所以，虽然是调用子线程的wait()方法，但是它是通过“主线程”去调用的；所以，休眠的是主线程，而不是“子线程”！</p>
<p>join()源码中 isAlive()检查此线程是否存活。</p>
<p>以上引用 <a href="https://www.cnblogs.com/skywang12345/p/3479275.html" target="_blank" rel="noopener">https://www.cnblogs.com/skywang12345/p/3479275.html</a></p>
<p>对join()方法的调用可以被中断，做法是在调用线程上调用interrupt()方法。<br><strong>注：谁加入 谁调用interrupt</strong></p>
<h4 id="21-2-12-创建有响应的用户界面"><a href="#21-2-12-创建有响应的用户界面" class="headerlink" title="21.2.12 创建有响应的用户界面"></a>21.2.12 创建有响应的用户界面</h4><p>使用线程的动机之一就是建立有响应的用户界面. 利用设置守护进程 setDaemon() 可以在等待用户输入的同时，后台操作浮点运算。 </p>
<h4 id="21-2-13-线程组"><a href="#21-2-13-线程组" class="headerlink" title="21.2.13 线程组"></a>21.2.13 线程组</h4><p>线程组持有一个线程集合。 ThreadGroup（最好把线程组堪称一次不成功的尝试，直接忽略！<br>！！）</p>
<h4 id="21-2-14-捕获异常"><a href="#21-2-14-捕获异常" class="headerlink" title="21.2.14 捕获异常"></a>21.2.14 捕获异常</h4><p>当单线程的程序发生一个未捕获的异常时我们可以采用try….catch进行异常的捕获，但是在多线程环境中，线程抛出的异常是不能用try….catch捕获的。<br>Thread.UncaughtExceptionHandler是Java SE5中的接口，允许在每个Thread对象上附着一个异常处理器。</p>
<p>使用示例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">package com.exception;</span><br><span class="line"> </span><br><span class="line">import java.lang.Thread.UncaughtExceptionHandler;</span><br><span class="line"> </span><br><span class="line">public class WitchCaughtThread</span><br><span class="line">&#123;</span><br><span class="line">	public static void main(String args[])</span><br><span class="line">	&#123;</span><br><span class="line">		Thread thread = new Thread(new Task());</span><br><span class="line">		thread.setUncaughtExceptionHandler(new ExceptionHandler());</span><br><span class="line">		thread.start();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line">class ExceptionHandler implements UncaughtExceptionHandler</span><br><span class="line">&#123;</span><br><span class="line">	@Override</span><br><span class="line">	public void uncaughtException(Thread t, Throwable e)</span><br><span class="line">	&#123;</span><br><span class="line">		System.out.println(&quot;==Exception: &quot;+e.getMessage());</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="21-3-共享受限资源"><a href="#21-3-共享受限资源" class="headerlink" title="21.3 共享受限资源"></a>21.3 共享受限资源</h2><h4 id="21-3-1-不正确地访问资源"><a href="#21-3-1-不正确地访问资源" class="headerlink" title="21.3.1 不正确地访问资源"></a>21.3.1 不正确地访问资源</h4><p>java 递增也不是原子性的，自身也需要多个步骤，因此不保护任务，单一递增也不是安全的。</p>
<h4 id="21-3-2-解决共享资源竞争"><a href="#21-3-2-解决共享资源竞争" class="headerlink" title="21.3.2 解决共享资源竞争"></a>21.3.2 解决共享资源竞争</h4><p>基本上所有的并发模式在解决线程冲突问题的时候，都是采用序列化访问共享资源的方案。这意味着在给定时刻只允许一个任务访问共享资源。</p>
<p>在使用并发时，将域设置为private是非常重要的。</p>
<p>使用显式的Lock对象</p>
<p>Lock对象必须被显式地创建、锁定和释放。</p>
<p>有了显式的Lock对象，就可以使用finnally子句将系统维护在正确的状态。</p>
<p><strong>Lock相比于synchronized具有更细粒度的控制力，以及处理异常，比如遍历列表中的节点的加锁机制（锁耦合），必须在当前节点解锁后捕获下一个锁。</strong></p>
<h4 id="21-3-3-原子性与易变性"><a href="#21-3-3-原子性与易变性" class="headerlink" title="21.3.3 原子性与易变性"></a>21.3.3 原子性与易变性</h4><p>原子操作是不能被线程调度机制中端的操作；一旦操作开始，那么它一定可以再发生的“上下文切换”之前（切换到其它线程执行）执行完毕。</p>
<p><strong>但是还是一种过于简化的机制，实际上也可能不安全，因为在多处理器上 还有可视性问题，例如一个任务作出修改，对其他任务也可能是不可视的，（修改只是暂时性存储本地处理器的缓存中），如果没有同步机制，那么修改时的可视将无法确定。</strong></p>
<p>如果一个域完全由synchronized方法或语句块来防护，那就不必将其设置为volatile。使用volatile而不是synchronized的唯一安全情况是类中只有一个可变的域。</p>
<p><strong>对基本类型的读取和赋值以及递增操作不是安全的原子性操作。反编译后将看到很多指令，get put 等等。</strong></p>
<h4 id="21-3-4-原子类"><a href="#21-3-4-原子类" class="headerlink" title="21.3.4 原子类"></a>21.3.4 原子类</h4><p>Atomic类被设计用来构建java.util.concurrent中的类，因此只有在特殊情况下才使用它们。如果涉及多个Atomic对象，可能就应该考虑放弃使用Atomic，通常依赖于锁要更安全一点。（要么是synchronized关键字，要么是显示的Lock对象）</p>
<h4 id="21-3-5-临界区"><a href="#21-3-5-临界区" class="headerlink" title="21.3.5 临界区"></a>21.3.5 临界区</h4><p>同步控制块 例如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">synchronized(syncObject) &#123;</span><br><span class="line">    // 临界区</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="21-3-6-在其它对象上同步"><a href="#21-3-6-在其它对象上同步" class="headerlink" title="21.3.6  在其它对象上同步"></a>21.3.6  在其它对象上同步</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">package synchronize;</span><br><span class="line"></span><br><span class="line">public class DualSynch &#123;</span><br><span class="line"></span><br><span class="line">    Object o = new Object();</span><br><span class="line">    public synchronized void f()&#123;</span><br><span class="line">        for (int i = 0; i &lt; 5; i++) &#123;</span><br><span class="line"></span><br><span class="line">            System.out.println(&quot;f()&quot;);</span><br><span class="line">            Thread.yield();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void g()&#123;</span><br><span class="line">        synchronized (o)&#123;</span><br><span class="line"></span><br><span class="line">            for (int i = 0; i &lt; 5; i++) &#123;</span><br><span class="line"></span><br><span class="line">                System.out.println(&quot;g()&quot;);</span><br><span class="line">                Thread.yield();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">package synchronize;</span><br><span class="line"></span><br><span class="line">public class SynObject &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        DualSynch dualSynch = new DualSynch();</span><br><span class="line"></span><br><span class="line">        new Thread()&#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run() &#123;</span><br><span class="line">                dualSynch.f();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;.start();</span><br><span class="line"></span><br><span class="line">        dualSynch.g();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>一个简答的例子验证了   synchronized 锁的内容。如果在this上同步，临界区的效果就会直接缩小在同步的范围内。两个任务可以同时进入同一个对象，只要这个对象上的方法是在不同的锁上同步的即可。<br><strong>synchronized代码块本质上完成的是代码片段的自动上锁和解锁，以确保关键代码片段在多线程中的互斥访问。synchronized既可以加在一段代码上，也可以加在方法上。</strong><br>（幼儿园解析）<br>synchronized 有三种形式 ：</p>
<ol>
<li><p>静态方法上的锁</p>
<p> 静态方法是属于“类”，不属于某个实例，是所有对象实例所共享的方法。也就是说如果在静态方法上加入synchronized，那么它获取的就是这个类的锁，<strong>锁住的就是这个类。</strong></p>
</li>
<li><p>实例方法（普通方法）上的锁</p>
<p> 实例方法并不是类所独有的，每个对象实例独立拥有它，它并不被对象实例所共享。这也比较能推出，在实例方法上加入synchronized，那么它获取的就是这个类的锁，<strong>锁住的就是这个对象实例。</strong></p>
</li>
<li><p>方法中使用同步代码块</p>
<ul>
<li>synchronized(this){…} this关键字所代表的意思是该对象实例，换句话说，这种用法synchronized锁住的仍然是对象实例，他和public synchronized void demo(){}可以说仅仅是做了语法上的改变。</li>
<li>private Object obj = new Object();    synchronized(obj){…}</li>
<li>synchronized(Demo.class){…}<br>这种形式等同于抢占获取类锁.收效甚微</li>
</ul>
</li>
</ol>
<p>以上引用：<a href="https://juejin.im/post/5d0c8101e51d455a694f954a" target="_blank" rel="noopener">https://juejin.im/post/5d0c8101e51d455a694f954a</a></p>
<h4 id="21-3-7-线程本地存储"><a href="#21-3-7-线程本地存储" class="headerlink" title="21.3.7 线程本地存储"></a>21.3.7 线程本地存储</h4><p>防止共享资源发生冲突的第二种方式就是根除对变量的共享。-&gt; java.lang.ThreadLocal 实现。</p>
<p>ThreadLocal 通常当作静态域存储，创建ThreadLocal时，只能通过get() set() 访问对象的内容。</p>
<p>原理解读：ThreadLocalMap是ThreadLocal的内部类，用Entry来进行存储</p>
<p>调用ThreadLocal的set()方法时，实际上就是往ThreadLocalMap设置值，key是ThreadLocal对象，值是传递进来的对象。</p>
<p><strong>所以简单说底层就是往Map存值，一个key 就是一个线程，就不会造成资源冲突了。</strong></p>
<h2 id="21-4-终结任务"><a href="#21-4-终结任务" class="headerlink" title="21.4 终结任务"></a>21.4 终结任务</h2><h4 id="21-4-1-装饰性花园"><a href="#21-4-1-装饰性花园" class="headerlink" title="21.4.1 装饰性花园"></a>21.4.1 装饰性花园</h4><p>本小节用了一大段代码说明了 多线程从统计学角度说失败的概率很小，导致很容易掉进坑里。所以要验证 要常用yield()，去增加失败率。可以用awaitTermination()方法来判断线程池中是否有继续运行的线程。</p>
<p>####21.4.2 在阻塞时终结</p>
<p>线程状态</p>
<ol>
<li>新建（new）</li>
<li>就绪（runnable）只要调度器把时间片分给线程就会执行。</li>
<li>阻塞（blocked）某个条件会阻止运行。当进入阻塞，调度器不会分配时间给线程，直到恢复就绪状态。</li>
<li>死亡（dead）结束或中断。</li>
</ol>
<p>进入阻塞状态</p>
<ol>
<li>通过调用sleep(milliseconds)使任务进入休眠状态。</li>
<li>调用wait()使线程挂起。直到线程得到了notify()或notifyAll()消息。</li>
<li>任务在等待某个输入/输出完成。（synchronized）</li>
<li>试图在某个对象上调用其同步控制方法，但对象锁不可用，因为另一个任务已经获得了这个锁。</li>
</ol>
<p>####21.4.3 中断</p>
<p>Thread interrupt()方法中止阻塞重的线程会抛出 InterruptedException() 异常。</p>
<p>concurrent类库避免对Thread对象的直接操作，转而通过Executor 执行所有操作。如果在Executor上调用shutdownNow()，它将发送一个interrupt()调用给它启动的所有线程。</p>
<p>使用Executor，那么通过调用submit()而不是execute()来启动任务，就可以持有该任务的上下文。submit()将返回一个泛型Future&lt;?&gt;，其中有一个未修饰的参数，因为你永远都不会在其上调用get()——持有这种Future的关键在于你可以在其上调用cancel()，并因此可以使用它来中断某个特定任务。如果你将true传递给cancel()，那么它就会拥有在该线程上调用interrupt()以停止这个线程的权限。因此，cancel()是一种中断由Executor启动的单个线程的方式。</p>
<p><strong>shutdown() 内部正在跑的任务和队列里等待的任务，会执行完。shutdownNow() 尝试将正在跑的任务interrupt中断.忽略队列里等待的任务,返回未执行的任务列表。</strong></p>
<p>不能中断正在试图获取synchronized锁或者试图执行I/O操作的线程。有一个不太好的解决方案：关闭任务在其上发生阻塞的底层资源。<br>例如 xx.close(); 关闭流资源。<br>（扩展基础）synchronized 释放 条件有4个：<br>    1. 当前线程的同步方法、代码块执行结束的时候释放。<br>    2. 当前线程在同步方法、同步代码块中遇到break 、 return 终于该代码块或者方法的时候释放。<br>    3. 出现未处理的error或者exception导致异常结束的时候释放<br>    4. 程序执行了 同步对象 wait 方法 ，当前线程暂停，释放锁</p>
<p>各种nio类提供了更人性化的I/O中断，被阻塞的nio通道会自动地响应中断。-&gt; SocketChannel.close();</p>
<p><strong>被互斥的阻塞</strong></p>
<p>一个任务能够调用在同一个对象中的其他的synchronized方法，因为这个任务已经持有锁了。</p>
<p>Java SE5并发类库添加了一个特性，即在ReentrantLock上阻塞的任务具备可以被中断的能力。</p>
<h4 id="21-4-4-检查中断"><a href="#21-4-4-检查中断" class="headerlink" title="21.4.4 检查中断"></a>21.4.4 检查中断</h4><p>Thread.interrupted()检查中断状态，不仅可以告诉你interrupt()是否被调用过，也可以清除中断状态。</p>
<p>try-finally子句使得无论run()循环如何退出，清理都会发生.</p>
<h2 id="21-5-线程之间的协作"><a href="#21-5-线程之间的协作" class="headerlink" title="21.5 线程之间的协作"></a>21.5 线程之间的协作</h2><p>当一个任务在方法里遇到了对wait()的调用的时候，线程的执行被挂起，<strong>对象上的锁被释放</strong>。<br>wait()、notify()和notifyAll()是基类Object的一部分。实际上，只能在同步控制方法或同步控制块里调用wait()、notify()和notifyAll()。</p>
<p><strong>错失的信号</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">thread1:</span><br><span class="line">synchronized(sharedMonitor)</span><br><span class="line">&#123;</span><br><span class="line">      someCondition = false;</span><br><span class="line">      sharedMonitor.notify();</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line">thread2:</span><br><span class="line">while(someCondition)</span><br><span class="line">&#123;</span><br><span class="line">	//point 1</span><br><span class="line">	synchronized(sharedMonitor)</span><br><span class="line">	&#123;</span><br><span class="line">		sharedMonitor.wait();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>先执行thread2，当进入while循环 执行thread1 会出现无限期等待。所以要把thread2代码块都同步。防止在在someCondition变量上产生竞争条件。</p>
<h4 id="21-5-2-notify-与notifyAll"><a href="#21-5-2-notify-与notifyAll" class="headerlink" title="21.5.2 notify()与notifyAll()"></a>21.5.2 notify()与notifyAll()</h4><p>在技术上，可能会有多个任务在单个Car对象上处于wait（）状态，notifyAll()更加安全。蛋实际上只会有一个任务处于wait(),因此用notify()替代notifyAll()。</p>
<p><strong>当前的线程不是此对象监视器的所有者。也就是要在当前线程锁定对象，才能用锁定的对象此行这些方法，需要用到synchronized ，锁定什么对象就用什么对象来执行  notify(), notifyAll(),wait(), wait(long), wait(long, int)操作，否则就会报IllegalMonitorStateException异常。</strong></p>
<p><strong>替代notifyAll() 将唤醒正在等待的所有任务是不严谨的，只是对当前线程锁定对象，等待这个锁的任务才会被唤醒。</strong></p>
<p>使用notify()，就必须保证被唤醒的是恰当的任务。</p>
<h4 id="21-5-3-生产者与消费者"><a href="#21-5-3-生产者与消费者" class="headerlink" title="21.5.3 生产者与消费者"></a>21.5.3 生产者与消费者</h4><p>生产者-消费者实现中，应使用先进先出队列来存储被生产和消费的对象。</p>
<p><strong>使用显式的Lock和Condition对象</strong></p>
<p>需要条件判断。而单个Lock将产生一个Condition对象，这个对象被用来管理任务间的通信。其他仍需要额外的表示处理状态信息。所以一个类中多个条件锁等待，需要用Condition()。</p>
<h4 id="21-5-4-生产者-消费者队列"><a href="#21-5-4-生产者-消费者队列" class="headerlink" title="21.5.4 生产者-消费者队列"></a>21.5.4 生产者-消费者队列</h4><p>wait() notifyAll() 以一种非常低级的方式解决了任务互操作问题，即每次交互时候都握手。多数情况下，可以使用更高级抽象级别，同步队列-&gt;(BlockingQueue，其内部是同步的）和系统的设计隐式地管理了任何时刻都只有一个任务在操作。<strong>但是遇到多消费者按顺序消费的需求用实现类LinkedBlockingQueue仍然需要外部加同步，它只是保证不会超消费，并不能保证多线程抢占顺序，还可以使用实现类SynchronousQueue。</strong></p>
<h4 id="21-5-5-任务间使用管道进行输入-输出"><a href="#21-5-5-任务间使用管道进行输入-输出" class="headerlink" title="21.5.5 任务间使用管道进行输入/输出"></a>21.5.5 任务间使用管道进行输入/输出</h4><p>PipedWriter/PipedReader 管道流通信核心是,Writer和Reader公用一块缓冲区,缓冲区在Reader中申请,由Writer调用和它绑定的Reader的Receive方法进行写. （BlockingQueue 使用起来会更加好。）</p>
<h2 id="21-6-死锁"><a href="#21-6-死锁" class="headerlink" title="21.6 死锁"></a>21.6 死锁</h2><p>任务之间相互等待的连续循环，没有哪个线程能继续。被称之为死锁。最可怕的是看起来工作良好，但是具有死锁危险。（很难重现的方式发生。）</p>
<p>死锁的条件：</p>
<ul>
<li>互斥条件</li>
<li>至少有一个任务必须持有一个资源且正在等待获取一个当前被别的任务持有的资源</li>
<li>资源不能被任务抢占，任务必须把资源释放当做普通事件</li>
<li>必须有循环等待</li>
</ul>
<p>破除一条就会解除，最容易的方法就是破坏第四个条件。</p>
<h2 id="21-7-新类库中的构件"><a href="#21-7-新类库中的构件" class="headerlink" title="21.7 新类库中的构件"></a>21.7 新类库中的构件</h2><h4 id="21-7-1-CountDownLatch"><a href="#21-7-1-CountDownLatch" class="headerlink" title="21.7.1 CountDownLatch"></a>21.7.1 CountDownLatch</h4><p>闭锁。被用来同步一个或多个任务，强制它们等待由其它任务执行的一组操作完成。<br>countDown 计数达到0, await() 调用会被阻塞。</p>
<h4 id="21-7-1-CyclicBarrier"><a href="#21-7-1-CyclicBarrier" class="headerlink" title="21.7.1 CyclicBarrier"></a>21.7.1 CyclicBarrier</h4><p>栅栏。CyclicBarrier用于等待其他线程运行到栅栏位置。与CountDownLatch 相似，CountDownLatch是只触发一次的事件，而CyclicBarrier可以多次重用。</p>
<h4 id="21-7-3-DelayQueue"><a href="#21-7-3-DelayQueue" class="headerlink" title="21.7.3 DelayQueue"></a>21.7.3 DelayQueue</h4><p>延时队列。无届的BlockingQueue，放置的对象实现了Delay接口对象，其中的对象只能在其到期时才能从队列取走。</p>
<h4 id="21-7-4-PriorityBlockingQueue"><a href="#21-7-4-PriorityBlockingQueue" class="headerlink" title="21.7.4 PriorityBlockingQueue"></a>21.7.4 PriorityBlockingQueue</h4><p>优先级队列PriorityBlockingQueue必须是实现Comparable接口，队列通过这个接口的compare方法确定对象的priority。当前和其他对象比较，如果compare方法返回负数，那么在队列里面的优先级就比较高。</p>
<h4 id="21-7-5-使用ScheduleExecutor的温室控制器"><a href="#21-7-5-使用ScheduleExecutor的温室控制器" class="headerlink" title="21.7.5 使用ScheduleExecutor的温室控制器"></a>21.7.5 使用ScheduleExecutor的温室控制器</h4><p>ScheduledThreadPoolExecutor 能解决在预定时间运行的任务。（线程池）</p>
<h4 id="21-7-6-Semaphore"><a href="#21-7-6-Semaphore" class="headerlink" title="21.7.6 Semaphore"></a>21.7.6 Semaphore</h4><p>正常的锁只能允许一个任务访问同一个资源。Semaphore可以控制某个资源可被同时访问的个数，通过 acquire() 获取一个许可，如果没有就等待，而 release() 释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。 </p>
<h4 id="21-7-7-Exchanger"><a href="#21-7-7-Exchanger" class="headerlink" title="21.7.7 Exchanger"></a>21.7.7 Exchanger</h4><p>应用场景是：一个任务在创建对象，这些对象的生产代价很高昂，而另一个任务在消费这些对象。通过这种方式，可以有更多的对象在被创建的同时被消费。<strong>创建完直接给其他线程处理消费，无需等待线程去选择执行。</strong></p>
<h2 id="21-8-仿真"><a href="#21-8-仿真" class="headerlink" title="21.8 仿真"></a>21.8 仿真</h2><p>个人理解：多线程可以模拟出真实应用场景的并发需求。</p>
<h2 id="21-9-性能调优"><a href="#21-9-性能调优" class="headerlink" title="21.9 性能调优"></a>21.9 性能调优</h2><p>用单线程测试 synchronized 要比 Lock 快很多,但是并没有意义。有可能会执行特殊优化，并且固定次数，可能会被编译器识别提前计算。所以要在多线程环境下测试。</p>
<h4 id="21-9-1-比较各类互斥技术"><a href="#21-9-1-比较各类互斥技术" class="headerlink" title="21.9.1 比较各类互斥技术"></a>21.9.1 比较各类互斥技术</h4><p>Lock通常会比使用synchronized要高效许多并且稳定。</p>
<p><strong>1.6后synchronized优化为锁升级，效率相近。</strong></p>
<h4 id="21-9-2-免锁容器"><a href="#21-9-2-免锁容器" class="headerlink" title="21.9.2 免锁容器"></a>21.9.2 免锁容器</h4><p>对容器的修改可以与读取操作同时发生，只要读取者只能看到完成修改的结果即可。</p>
<p>CopyOnWriteArrayList/CopyOnWriteSet/ConcurrentHashMap 都可称为免锁容器</p>
<p><strong>只有set使用synchronized，原理是在容器数据结构复制出一个单独的副本，修改结束后与主数据交换。使新读操作可以看到这个修改。容器只有部分内容而不是整个内容可以被复制和修改。</strong></p>
<p>源码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">public boolean add(E e) &#123;</span><br><span class="line">        synchronized (lock) &#123;</span><br><span class="line">            Object[] es = getArray();</span><br><span class="line">            int len = es.length;</span><br><span class="line">            es = Arrays.copyOf(es, len + 1);</span><br><span class="line">            es[len] = e;</span><br><span class="line">            setArray(es);</span><br><span class="line">            return true;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>等到修改结束，读操作才能看到。</p>
<p><strong>乐观锁</strong></p>
<p>synchronized ArrayList 无论读写数量多少，性能都大致相同的慢。CopyOnWriteArrayList对写少的情况喜爱回异常的快，并且在多写情况下虽然慢但也要比 整个列表加锁性能要好的多。</p>
<p>*<em>比较各种Map实现  *</em></p>
<p>ConcurrentHashMap 比synchronized HashMap 性能要好很多，因为使用分段锁，每次只锁一个Entry的key值。</p>
<h4 id="21-9-3-乐观加锁"><a href="#21-9-3-乐观加锁" class="headerlink" title="21.9.3 乐观加锁"></a>21.9.3 乐观加锁</h4><p>Atomic对象的compareAndSet() 比较并交换 不需要互斥锁，性能更高，但是比较失败 需要重续操作，比较消耗性能<br>someone.compareAndSet(false,true)可以理解为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">if(someone == false)&#123;</span><br><span class="line">    someone=true</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="21-9-4-ReadWriteLock"><a href="#21-9-4-ReadWriteLock" class="headerlink" title="21.9.4 ReadWriteLock"></a>21.9.4 ReadWriteLock</h4><p>对向数据结构相对不频繁的写入，但是有多个任务要经常读取这个数据结构的这类情况进行了优化。</p>
<h2 id="21-10-活动对象"><a href="#21-10-活动对象" class="headerlink" title="21.10 活动对象"></a>21.10 活动对象</h2><p>活动对象的特征：</p>
<ul>
<li>基于消息机制：<br>对活动对象的请求和可能的参数都被转化为消息，这些消息被转发给活动对象实际实现并排队等待处理。处理结果以future对象返还给提出请求的对象。</li>
</ul>
<ul>
<li><p>异步调用：<br>对活动对象的请求被异步执行，实际由活动对象的工作线程处理请求，故不会阻塞调用者。仅当请求未完成执行时，调用者试图获取处理结果时会被阻塞。</p>
</li>
<li><p>线程安全：<br>活动对象天生是线程安全的。因为他顺序地从请求队列摘取消息进行处理，并且始终在一个单独的线程中执行，由于不存在资源竞争，所以也不用担心同步、死锁等问题。同步仍旧会发生，但它通过将方法调用排队，使得任何时刻都只能发生一个调用，从而将同步控制在消息的级别上发生。<br>每个Future对象 在维护着它自己的工作器线程和消息队列，并且所有对这种对象的请求都将进入队列排队，任何时刻只运行一个。</p>
</li>
</ul>
<p>应用场景：<br>适合于按某一特定顺序执行而又互不影响的调用者执行状况的情景。</p>
<h2 id="21-11-总结"><a href="#21-11-总结" class="headerlink" title="21.11 总结"></a>21.11 总结</h2><p> 线程提供轻量级执行上下文切换（大约100条指令。）<br> 进程是重量的（大约1000条。）<br> 线程缺点：</p>
<ol>
<li><p>等待共享资源性能降低</p>
</li>
<li><p>需要处理线程的额外CPU花费。</p>
</li>
<li><p>糟糕的程序设计导致不必要饿复杂度。</p>
</li>
<li><p>有可能产生一些病态行为，如饿死、竞争、死锁、活锁（多个运行各自任务的线程使得整体无法完成。）</p>
</li>
<li><p>不同平台导致不一致性。</p>
<p>多线程共享资源，要确保不会同时读取或改变资源，明知的使用加锁机制。要对锁有透彻的理解，否则会引入潜在的死锁。</p>
<p>如果线程问题变得大而复杂，就要使用erlang语言。专门用于线程机制的函数型语言。</p>
</li>
</ol>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2020/03/01/Thinking-in-Java-第二十章/" rel="next" title="Thinking in Java 第二十章">
                <i class="fa fa-chevron-left"></i> Thinking in Java 第二十章
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2020/03/17/Spring-Cloud-简介-Spring-Boot-快速理解使用/" rel="prev" title="Spring Cloud 简介 & Spring Boot 快速理解使用">
                Spring Cloud 简介 & Spring Boot 快速理解使用 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            Table of Contents
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            Overview
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image" src="/images/avatar.jpg" alt="190coder">
            
              <p class="site-author-name" itemprop="name">190coder</p>
              <p class="site-description motion-element" itemprop="description">坚信持续学习就一定能超过大多数人</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">53</span>
                  <span class="site-state-item-name">posts</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">13</span>
                  <span class="site-state-item-name">categories</span>
                </a>
              </div>
            

            

          </nav>

          

          

          
          

          
          

          <div style>
  <canvas id="canvas" style="width:60%;">当前浏览器不支持canvas，请更换浏览器后再试</canvas>
</div>
<script>
(function(){

   var digit=
    [
        [
            [0,0,1,1,1,0,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,0,1,1,0],
            [0,0,1,1,1,0,0]
        ],//0
        [
            [0,0,0,1,1,0,0],
            [0,1,1,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [1,1,1,1,1,1,1]
        ],//1
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,0,0,0],
            [0,1,1,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,0,0,0,1,1],
            [1,1,1,1,1,1,1]
        ],//2
        [
            [1,1,1,1,1,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,0,0,1,1,0],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//3
        [
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,1,0],
            [0,0,1,1,1,1,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,1,1,0],
            [1,1,1,1,1,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,1,1]
        ],//4
        [
            [1,1,1,1,1,1,1],
            [1,1,0,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,1,1,1,1,0],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//5
        [
            [0,0,0,0,1,1,0],
            [0,0,1,1,0,0,0],
            [0,1,1,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,0,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//6
        [
            [1,1,1,1,1,1,1],
            [1,1,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0]
        ],//7
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//8
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,1,1,0,0,0,0]
        ],//9
        [
            [0,0,0,0,0,0,0],
            [0,0,1,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,1,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,0,0,0,0,0]
        ]//:
    ];

var canvas = document.getElementById('canvas');

if(canvas.getContext){
    var cxt = canvas.getContext('2d');
    //声明canvas的宽高
    var H = 100,W = 700;
    canvas.height = H;
    canvas.width = W;
    cxt.fillStyle = '#f00';
    cxt.fillRect(10,10,50,50);

    //存储时间数据
    var data = [];
    //存储运动的小球
    var balls = [];
    //设置粒子半径
    var R = canvas.height/20-1;
    (function(){
        var temp = /(\d)(\d):(\d)(\d):(\d)(\d)/.exec(new Date());
        //存储时间数字，由十位小时、个位小时、冒号、十位分钟、个位分钟、冒号、十位秒钟、个位秒钟这7个数字组成
        data.push(temp[1],temp[2],10,temp[3],temp[4],10,temp[5],temp[6]);
    })();

    /*生成点阵数字*/
    function renderDigit(index,num){
        for(var i = 0; i < digit[num].length; i++){
            for(var j = 0; j < digit[num][i].length; j++){
                if(digit[num][i][j] == 1){
                    cxt.beginPath();
                    cxt.arc(14*(R+2)*index + j*2*(R+1)+(R+1),i*2*(R+1)+(R+1),R,0,2*Math.PI);
                    cxt.closePath();
                    cxt.fill();
                }
            }
        }
    }

    /*更新时钟*/
    function updateDigitTime(){
        var changeNumArray = [];
        var temp = /(\d)(\d):(\d)(\d):(\d)(\d)/.exec(new Date());
        var NewData = [];
        NewData.push(temp[1],temp[2],10,temp[3],temp[4],10,temp[5],temp[6]);
        for(var i = data.length-1; i >=0 ; i--){
            //时间发生变化
            if(NewData[i] !== data[i]){
                //将变化的数字值和在data数组中的索引存储在changeNumArray数组中
                changeNumArray.push(i+'_'+(Number(data[i])+1)%10);
            }
        }
        //增加小球
        for(var i = 0; i< changeNumArray.length; i++){
            addBalls.apply(this,changeNumArray[i].split('_'));
        }
        data = NewData.concat();
    }

    /*更新小球状态*/
    function updateBalls(){
        for(var i = 0; i < balls.length; i++){
            balls[i].stepY += balls[i].disY;
            balls[i].x += balls[i].stepX;
            balls[i].y += balls[i].stepY;
            if(balls[i].x > W + R || balls[i].y > H + R){
                balls.splice(i,1);
                i--;
            }
        }
    }

    /*增加要运动的小球*/
    function addBalls(index,num){
        var numArray = [1,2,3];
        var colorArray =  ["#3BE","#09C","#A6C","#93C","#9C0","#690","#FB3","#F80","#F44","#C00"];
        for(var i = 0; i < digit[num].length; i++){
            for(var j = 0; j < digit[num][i].length; j++){
                if(digit[num][i][j] == 1){
                    var ball = {
                        x:14*(R+2)*index + j*2*(R+1)+(R+1),
                        y:i*2*(R+1)+(R+1),
                        stepX:Math.floor(Math.random() * 4 -2),
                        stepY:-2*numArray[Math.floor(Math.random()*numArray.length)],
                        color:colorArray[Math.floor(Math.random()*colorArray.length)],
                        disY:1
                    };
                    balls.push(ball);
                }
            }
        }
    }

    /*渲染*/
    function render(){
        //重置画布宽度，达到清空画布的效果
        canvas.height = 100;
        //渲染时钟
        for(var i = 0; i < data.length; i++){
            renderDigit(i,data[i]);
        }
        //渲染小球
        for(var i = 0; i < balls.length; i++){
            cxt.beginPath();
            cxt.arc(balls[i].x,balls[i].y,R,0,2*Math.PI);
            cxt.fillStyle = balls[i].color;
            cxt.closePath();
            cxt.fill();
        }
    }

    clearInterval(oTimer);
    var oTimer = setInterval(function(){
        //更新时钟
        updateDigitTime();
        //更新小球状态
        updateBalls();
        //渲染
        render();
    },50);
}

})();
</script>



        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#第二十一章-并发"><span class="nav-number">1.</span> <span class="nav-text">第二十一章 并发</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#21-1-并发的多面性"><span class="nav-number">1.1.</span> <span class="nav-text">21.1 并发的多面性</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#21-1-1-更快的执行"><span class="nav-number">1.1.0.1.</span> <span class="nav-text">21.1.1 更快的执行</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-1-2-改进代码设计"><span class="nav-number">1.1.0.2.</span> <span class="nav-text">21.1.2 改进代码设计</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-2-基本的线程机制"><span class="nav-number">1.2.</span> <span class="nav-text">21.2 基本的线程机制</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-1-定义任务"><span class="nav-number">1.2.0.1.</span> <span class="nav-text">21.2.1 定义任务</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-2-Thread-类"><span class="nav-number">1.2.0.2.</span> <span class="nav-text">21.2.2 Thread 类</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-3-使用Executor"><span class="nav-number">1.2.0.3.</span> <span class="nav-text">21.2.3 使用Executor</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-4-从任务中产生返回值"><span class="nav-number">1.2.0.4.</span> <span class="nav-text">21.2.4 从任务中产生返回值</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-5-休眠"><span class="nav-number">1.2.0.5.</span> <span class="nav-text">21.2.5 休眠</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-6-优先级"><span class="nav-number">1.2.0.6.</span> <span class="nav-text">21.2.6 优先级</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-7-让步"><span class="nav-number">1.2.0.7.</span> <span class="nav-text">21.2.7 让步</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-8-后台程序"><span class="nav-number">1.2.0.8.</span> <span class="nav-text">21.2.8 后台程序</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-9-编码的变体"><span class="nav-number">1.2.0.9.</span> <span class="nav-text">21.2.9 编码的变体</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-10-术语"><span class="nav-number">1.2.0.10.</span> <span class="nav-text">21.2.10 术语</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-11-加入一个线程"><span class="nav-number">1.2.0.11.</span> <span class="nav-text">21.2.11 加入一个线程</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-12-创建有响应的用户界面"><span class="nav-number">1.2.0.12.</span> <span class="nav-text">21.2.12 创建有响应的用户界面</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-13-线程组"><span class="nav-number">1.2.0.13.</span> <span class="nav-text">21.2.13 线程组</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-2-14-捕获异常"><span class="nav-number">1.2.0.14.</span> <span class="nav-text">21.2.14 捕获异常</span></a></li></ol></li></ol><li class="nav-item nav-level-2"><a class="nav-link" href="#21-3-共享受限资源"><span class="nav-number">1.3.</span> <span class="nav-text">21.3 共享受限资源</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#21-3-1-不正确地访问资源"><span class="nav-number">1.3.0.1.</span> <span class="nav-text">21.3.1 不正确地访问资源</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-3-2-解决共享资源竞争"><span class="nav-number">1.3.0.2.</span> <span class="nav-text">21.3.2 解决共享资源竞争</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-3-3-原子性与易变性"><span class="nav-number">1.3.0.3.</span> <span class="nav-text">21.3.3 原子性与易变性</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-3-4-原子类"><span class="nav-number">1.3.0.4.</span> <span class="nav-text">21.3.4 原子类</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-3-5-临界区"><span class="nav-number">1.3.0.5.</span> <span class="nav-text">21.3.5 临界区</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-3-6-在其它对象上同步"><span class="nav-number">1.3.0.6.</span> <span class="nav-text">21.3.6  在其它对象上同步</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-3-7-线程本地存储"><span class="nav-number">1.3.0.7.</span> <span class="nav-text">21.3.7 线程本地存储</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-4-终结任务"><span class="nav-number">1.4.</span> <span class="nav-text">21.4 终结任务</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#21-4-1-装饰性花园"><span class="nav-number">1.4.0.1.</span> <span class="nav-text">21.4.1 装饰性花园</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-4-4-检查中断"><span class="nav-number">1.4.0.2.</span> <span class="nav-text">21.4.4 检查中断</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-5-线程之间的协作"><span class="nav-number">1.5.</span> <span class="nav-text">21.5 线程之间的协作</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#21-5-2-notify-与notifyAll"><span class="nav-number">1.5.0.1.</span> <span class="nav-text">21.5.2 notify()与notifyAll()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-5-3-生产者与消费者"><span class="nav-number">1.5.0.2.</span> <span class="nav-text">21.5.3 生产者与消费者</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-5-4-生产者-消费者队列"><span class="nav-number">1.5.0.3.</span> <span class="nav-text">21.5.4 生产者-消费者队列</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-5-5-任务间使用管道进行输入-输出"><span class="nav-number">1.5.0.4.</span> <span class="nav-text">21.5.5 任务间使用管道进行输入/输出</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-6-死锁"><span class="nav-number">1.6.</span> <span class="nav-text">21.6 死锁</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-7-新类库中的构件"><span class="nav-number">1.7.</span> <span class="nav-text">21.7 新类库中的构件</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#21-7-1-CountDownLatch"><span class="nav-number">1.7.0.1.</span> <span class="nav-text">21.7.1 CountDownLatch</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-7-1-CyclicBarrier"><span class="nav-number">1.7.0.2.</span> <span class="nav-text">21.7.1 CyclicBarrier</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-7-3-DelayQueue"><span class="nav-number">1.7.0.3.</span> <span class="nav-text">21.7.3 DelayQueue</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-7-4-PriorityBlockingQueue"><span class="nav-number">1.7.0.4.</span> <span class="nav-text">21.7.4 PriorityBlockingQueue</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-7-5-使用ScheduleExecutor的温室控制器"><span class="nav-number">1.7.0.5.</span> <span class="nav-text">21.7.5 使用ScheduleExecutor的温室控制器</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-7-6-Semaphore"><span class="nav-number">1.7.0.6.</span> <span class="nav-text">21.7.6 Semaphore</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-7-7-Exchanger"><span class="nav-number">1.7.0.7.</span> <span class="nav-text">21.7.7 Exchanger</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-8-仿真"><span class="nav-number">1.8.</span> <span class="nav-text">21.8 仿真</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-9-性能调优"><span class="nav-number">1.9.</span> <span class="nav-text">21.9 性能调优</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#21-9-1-比较各类互斥技术"><span class="nav-number">1.9.0.1.</span> <span class="nav-text">21.9.1 比较各类互斥技术</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-9-2-免锁容器"><span class="nav-number">1.9.0.2.</span> <span class="nav-text">21.9.2 免锁容器</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-9-3-乐观加锁"><span class="nav-number">1.9.0.3.</span> <span class="nav-text">21.9.3 乐观加锁</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#21-9-4-ReadWriteLock"><span class="nav-number">1.9.0.4.</span> <span class="nav-text">21.9.4 ReadWriteLock</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-10-活动对象"><span class="nav-number">1.10.</span> <span class="nav-text">21.10 活动对象</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-11-总结"><span class="nav-number">1.11.</span> <span class="nav-text">21.11 总结</span></a></li></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

	
           
         
         <div style>
  <canvas id="canvas" style="width:60%;">当前浏览器不支持canvas，请更换浏览器后再试</canvas>
</div>
<script>
(function(){

   var digit=
    [
        [
            [0,0,1,1,1,0,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,0,1,1,0],
            [0,0,1,1,1,0,0]
        ],//0
        [
            [0,0,0,1,1,0,0],
            [0,1,1,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [1,1,1,1,1,1,1]
        ],//1
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,0,0,0],
            [0,1,1,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,0,0,0,1,1],
            [1,1,1,1,1,1,1]
        ],//2
        [
            [1,1,1,1,1,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,0,0,1,1,0],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//3
        [
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,1,0],
            [0,0,1,1,1,1,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,1,1,0],
            [1,1,1,1,1,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,1,1]
        ],//4
        [
            [1,1,1,1,1,1,1],
            [1,1,0,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,1,1,1,1,0],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//5
        [
            [0,0,0,0,1,1,0],
            [0,0,1,1,0,0,0],
            [0,1,1,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,0,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//6
        [
            [1,1,1,1,1,1,1],
            [1,1,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0]
        ],//7
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//8
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,1,1,0,0,0,0]
        ],//9
        [
            [0,0,0,0,0,0,0],
            [0,0,1,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,1,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,0,0,0,0,0]
        ]//:
    ];

var canvas = document.getElementById('canvas');

if(canvas.getContext){
    var cxt = canvas.getContext('2d');
    //声明canvas的宽高
    var H = 100,W = 700;
    canvas.height = H;
    canvas.width = W;
    cxt.fillStyle = '#f00';
    cxt.fillRect(10,10,50,50);

    //存储时间数据
    var data = [];
    //存储运动的小球
    var balls = [];
    //设置粒子半径
    var R = canvas.height/20-1;
    (function(){
        var temp = /(\d)(\d):(\d)(\d):(\d)(\d)/.exec(new Date());
        //存储时间数字，由十位小时、个位小时、冒号、十位分钟、个位分钟、冒号、十位秒钟、个位秒钟这7个数字组成
        data.push(temp[1],temp[2],10,temp[3],temp[4],10,temp[5],temp[6]);
    })();

    /*生成点阵数字*/
    function renderDigit(index,num){
        for(var i = 0; i < digit[num].length; i++){
            for(var j = 0; j < digit[num][i].length; j++){
                if(digit[num][i][j] == 1){
                    cxt.beginPath();
                    cxt.arc(14*(R+2)*index + j*2*(R+1)+(R+1),i*2*(R+1)+(R+1),R,0,2*Math.PI);
                    cxt.closePath();
                    cxt.fill();
                }
            }
        }
    }

    /*更新时钟*/
    function updateDigitTime(){
        var changeNumArray = [];
        var temp = /(\d)(\d):(\d)(\d):(\d)(\d)/.exec(new Date());
        var NewData = [];
        NewData.push(temp[1],temp[2],10,temp[3],temp[4],10,temp[5],temp[6]);
        for(var i = data.length-1; i >=0 ; i--){
            //时间发生变化
            if(NewData[i] !== data[i]){
                //将变化的数字值和在data数组中的索引存储在changeNumArray数组中
                changeNumArray.push(i+'_'+(Number(data[i])+1)%10);
            }
        }
        //增加小球
        for(var i = 0; i< changeNumArray.length; i++){
            addBalls.apply(this,changeNumArray[i].split('_'));
        }
        data = NewData.concat();
    }

    /*更新小球状态*/
    function updateBalls(){
        for(var i = 0; i < balls.length; i++){
            balls[i].stepY += balls[i].disY;
            balls[i].x += balls[i].stepX;
            balls[i].y += balls[i].stepY;
            if(balls[i].x > W + R || balls[i].y > H + R){
                balls.splice(i,1);
                i--;
            }
        }
    }

    /*增加要运动的小球*/
    function addBalls(index,num){
        var numArray = [1,2,3];
        var colorArray =  ["#3BE","#09C","#A6C","#93C","#9C0","#690","#FB3","#F80","#F44","#C00"];
        for(var i = 0; i < digit[num].length; i++){
            for(var j = 0; j < digit[num][i].length; j++){
                if(digit[num][i][j] == 1){
                    var ball = {
                        x:14*(R+2)*index + j*2*(R+1)+(R+1),
                        y:i*2*(R+1)+(R+1),
                        stepX:Math.floor(Math.random() * 4 -2),
                        stepY:-2*numArray[Math.floor(Math.random()*numArray.length)],
                        color:colorArray[Math.floor(Math.random()*colorArray.length)],
                        disY:1
                    };
                    balls.push(ball);
                }
            }
        }
    }

    /*渲染*/
    function render(){
        //重置画布宽度，达到清空画布的效果
        canvas.height = 100;
        //渲染时钟
        for(var i = 0; i < data.length; i++){
            renderDigit(i,data[i]);
        }
        //渲染小球
        for(var i = 0; i < balls.length; i++){
            cxt.beginPath();
            cxt.arc(balls[i].x,balls[i].y,R,0,2*Math.PI);
            cxt.fillStyle = balls[i].color;
            cxt.closePath();
            cxt.fill();
        }
    }

    clearInterval(oTimer);
    var oTimer = setInterval(function(){
        //更新时钟
        updateDigitTime();
        //更新小球状态
        updateBalls();
        //渲染
        render();
    },50);
}

})();
</script>



    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">190coder</span>

  
</div>


  <div class="powered-by">Powered by <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a></div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">Theme &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>





<div>
    <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <span id="busuanzi_container_site_pv">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
    <span class="post-meta-divider">|</span>
    <span id="busuanzi_container_site_uv">本站访客数<span id="busuanzi_value_site_uv"></span>人</span>
</div>


        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  


  











  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  

  
  
    <script type="text/javascript" src="//cdn.jsdelivr.net/gh/theme-next/theme-next-canvas-nest@1/canvas-nest.min.js"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

</body>
</html>
